package org.hs.phd.odi.bgsub.features;

import java.util.ArrayList;
import java.util.List;

import org.hs.phd.odi.bgsub.vob.VisualObject;
import org.hs.phd.odi.bgsub.vob.VisualObjectFeatures;

import com.googlecode.javacv.cpp.opencv_core.CvMat;
import com.googlecode.javacv.cpp.opencv_features2d;
import com.googlecode.javacv.cpp.opencv_features2d.DMatch;

public class OpenCVFeatureMatcher implements FeatureMatcher {

	@Override
	public VisualObject findBestMatch(VisualObject searchedObject, List< ? extends VisualObject> list) {
		VisualObject currentBestMatch = null;
		float currentBestMatchSimilarity = 0;
		
		for(VisualObject candidateMatch: list){
			float similarity = calculateSimilarity(searchedObject.getFeatures(), candidateMatch.getFeatures());
			
			if(similarity > currentBestMatchSimilarity ){
				currentBestMatch = candidateMatch;
				currentBestMatchSimilarity = similarity;
			}
		}
		
		return currentBestMatch;
	}

	private static float calculateSimilarity(VisualObjectFeatures features1, VisualObjectFeatures features2) {
		CvFeatures surf1 = (CvFeatures) features1;
		CvFeatures surf2 = (CvFeatures) features2;

		List<DMatch> pairs = flannFindPairs(surf1, surf2);
		
		return pairs.size();
	}

	
	
	private static List<DMatch> flannFindPairs(CvFeatures surf1, CvFeatures surf2) {

		CvMat descriptors1 = surf1.getDescriptors();
		CvMat descriptors2 = surf2.getDescriptors();

		DMatch matches = new DMatch();
		//opencv_features2d.FlannBasedMatcher matcher = new opencv_features2d.FlannBasedMatcher();
		opencv_features2d.FlannBasedMatcher matcher = new opencv_features2d.FlannBasedMatcher();
		matcher.match(descriptors1, descriptors2, matches, null);
		//matcher.radiusMatch(descriptors1, descriptors2, matches, 0.8, null, false);

		double maxDist = Double.MIN_VALUE;
		double minDist = Double.MAX_VALUE;

		// -- Quick calculation of max and min distances between keypoints
		for (int i = 0; i < matches.capacity(); i++) {
			double dist = ((DMatch) matches.position(i)).distance();
			if (dist < minDist)
				minDist = dist;
			if (dist > maxDist)
				maxDist = dist;
		}

		List<DMatch> goodMatches = new ArrayList<opencv_features2d.DMatch>();
		for (int i = 0; i < descriptors1.rows(); i++) {
			double dist = ((DMatch) matches.position(i)).distance();

			if (dist < 2 * minDist) {
				goodMatches.add((DMatch) matches.position(i));
			}
		}

		return goodMatches;
	}
}
